package arkham.util;

import java.awt.Color;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import arkham.jaxb.encounter.*;
import arkham.data.*;

/**
 * These are a set of tools for assistance with the JAXB classes.
 * @author ctheng
 *
 */
public class XmlTools {

	public static String unescapeString(String s)
	{
		String s2 = s.replaceAll(Matcher.quoteReplacement("\\n"), "\n");
		s2 = s2.replaceAll("&quot;", "\"");
		return s2;
	}

	/**
	 * Checks if a string contains any chars from the other string
	 * @param s The string to check
	 * @param chars The string with the chars
	 * @return true if it does contain chars from the string, false otherwise
	 */
	public static boolean containsChars(String s, String chars)
	{
		for (int i = 0; i < chars.length(); i++)
		{
			char q = chars.charAt(i);
			if (s.indexOf(q) != -1) return true;
		}
		return false;
	}

	/**
	 * Checks if a string contains only alphanumeric characters and underscore
	 * @param s The string to check
	 * @return true if it contains only letters, numbers and underscore
	 */
	public static boolean isExtAlphaNumeric(String s)
	{
		return Pattern.matches("\\w+", s);
	}

	/**
	 * Test if a String is numeric
	 * @param s The string to test
	 * @return true if it is, false otherwise
	 */
	public static boolean isNumeric(String s)
	{
		try
		{
			int a = Integer.parseInt(s);
			return true;
		}
		catch (NumberFormatException e)
		{
			return false;
		}
	}

	/**
	 * Parse int taking into account initial + signs
	 * @param s the string to parse
	 * @return the parsed int
	 * @throws NumberFormatException if s is not an integer
	 */
	public static int parseInt(String s)
	{
	    if (s.startsWith("+"))
	    {
	        return Integer.parseInt(s.substring(1));
	    }
	    return Integer.parseInt(s);
	}

	/**
	 * Formats numbers in the format of a modifier: adds +
	 * to 0 and positive numbers, changes
	 * @param value the actual modifier
	 * @return the string presentation of the modifier
	 */
	public static String getModifier(int value)
	{
		if (value == Globals.SPECIAL_MODIFIER || value == -Globals.SPECIAL_MODIFIER)
		{
			return "Special";
		}
		if (value < 0)
		{
			return String.valueOf(value);
		}
		else
		{
			return "+" + String.valueOf(value);
		}
	}

	/**
	 * Pads a string with spaces till the desired width
	 * @param s the string to pad
	 * @param width the desired width
	 * @return the string padded with enough spaces to make the width, or
	 * the original string if the original width is equal or higher than
	 * the desired width
	 */
	public static String pad(String s, int width)
	{
		StringBuffer sb = new StringBuffer(s);
		while (sb.length() < width)
		{
			sb.append(" ");
		}
		return sb.toString();
	}

	/**
	 * Truncates a string to a max of a specified length and fills in an
	 * appropriate number of dots at the end if too long
	 * @param s the string
	 * @param width the maximum length
	 * @param dots the number of dots
	 * @return the truncated string
	 */
	public static String truncate(String s, int width, int dots)
	{
		if (s.length() > width)
		{
			s = s.substring(0, width - dots);
			for (int i = 0; i < dots; i++)
			{
				s += ".";
			}
		}
		return s;
	}

	/**
	 * Checks if a property is defined for a card
	 * @param card The card XML representation
	 * @param propertyName The name of the property
	 * @return true if the property is defined, false otherwise
	 */
	public static boolean isPropertyDefined (CardType card, String propertyName)
	{
		for (PropertyType proptype : card.getProperty())
		{
			if (proptype.getName().equalsIgnoreCase(propertyName))
			{
				return true;
			}
		}
		return false;
	}

	/**
     * Checks if a property is defined for a personal effect
     * @param peff The personal effect XML representation
     * @param propertyName The name of the property
     * @return true if the property is defined, false otherwise
     */
    public static boolean isPropertyDefined (PersonalEffectType peff, String propertyName)
    {
        for (PropertyType proptype : peff.getProperty())
        {
            if (proptype.getName().equalsIgnoreCase(propertyName))
            {
                return true;
            }
        }
        return false;
    }

	/**
	 * Gets the value of a card property in string format
	 * @param card The card XML representation
	 * @param propertyName The name of the property
	 * @return The string value, or null if it is not defined
	 */
	public static String getStrPropertyValue (CardType card, String propertyName)
	{
		for (PropertyType proptype : card.getProperty())
		{
			if (proptype.getName().equalsIgnoreCase(propertyName))
			{
				return proptype.getValue();
			}
		}
		return null;
	}

	/**
     * Gets the value of a property in string format
     * @param peff The personal effect XML representation
     * @param propertyName The name of the property
     * @return The string value, or null if it is not defined
     */
    public static String getStrPropertyValue (PersonalEffectType peff, String propertyName)
    {
        for (PropertyType proptype : peff.getProperty())
        {
            if (proptype.getName().equalsIgnoreCase(propertyName))
            {
                return proptype.getValue();
            }
        }
        return null;
    }

	/**
	 * Gets the value of a card property in int format
	 * @param card The card XML representation
	 * @param propertyName The name of the property
	 * @return The int value
	 * @throws NumberFormatException if not in the right format or not defined.
	 */
	public static int getIntPropertyValue(CardType card, String propertyName)
	{
		for (PropertyType proptype : card.getProperty())
		{
			if (proptype.getName().equalsIgnoreCase(propertyName))
			{
				return Integer.parseInt(proptype.getValue());
			}
		}
		throw new NumberFormatException("Property " + propertyName +
				" not defined for " + card.getName());
	}

	/**
     * Gets the value of a property in int format
     * @param peff The personal effect XML representation
     * @param propertyName The name of the property
     * @return The int value
     * @throws NumberFormatException if not in the right format or not defined.
     */
    public static int getIntPropertyValue(PersonalEffectType peff, String propertyName)
    {
        for (PropertyType proptype : peff.getProperty())
        {
            if (proptype.getName().equalsIgnoreCase(propertyName))
            {
                return Integer.parseInt(proptype.getValue());
            }
        }
        throw new NumberFormatException("Property " + propertyName +
                " not defined for " + peff.getName());
    }

	/**
	 * Concatenates a string array into a CSV string, starting from a particular index
	 * @param strArray the string array
	 * @param index the index to start from
	 * @return the concatenated string
	 */
	public static String concatArray(String[] strArray, int index)
	{
	    StringBuffer sb = new StringBuffer();
	    for (int i = index; i < strArray.length; i++)
	    {
	        if (i != index)
	        {
	            sb.append(",");
	        }
	        sb.append(strArray[i]);
	    }
	    return sb.toString();
	}

	public final static String[] SUPPORTED_COLORS = new String[]{
		"black", "white", "red", "orange", "yellow",
		"green", "pink", "magenta", "blue", "cyan",
		"purple", "dark purple", "grey", "light gray", "brown",
		"light blue", "dark green", "dark orange", "light pink",
		"light green"
	};

	/**
	 * Interpret a string description of a color into a color object
	 * @param strcolor The color in words
	 * @return The Color object corresponding to the color in words. The default color
	 * is white if the color cannot be parsed
	 */
	public static Color translateColorString(String strcolor)
	{
		if (strcolor.equalsIgnoreCase("black")) return Color.black;
		if (strcolor.equalsIgnoreCase("white")) return Color.white;
		if (strcolor.equalsIgnoreCase("red")) return Color.red;
		if (strcolor.equalsIgnoreCase("orange")) return Color.orange;
		if (strcolor.equalsIgnoreCase("dark orange")) return new Color(160, 80, 0);
		if (strcolor.equalsIgnoreCase("yellow")) return Color.yellow;
		if (strcolor.equalsIgnoreCase("green")) return Color.green;
		if (strcolor.equalsIgnoreCase("pink")) return Color.pink;
		if (strcolor.equalsIgnoreCase("magenta")) return Color.magenta;
		if (strcolor.equalsIgnoreCase("blue")) return Color.blue;
		if (strcolor.equalsIgnoreCase("cyan")) return Color.cyan;
		if (strcolor.equalsIgnoreCase("purple")) return new Color(128, 0, 255);
		if (strcolor.equalsIgnoreCase("dark purple")) return new Color(64, 0, 128);
		if (strcolor.equalsIgnoreCase("grey")) return Color.gray;
		if (strcolor.equalsIgnoreCase("gray")) return Color.gray;
		if (strcolor.equalsIgnoreCase("light gray")) return new Color(200, 200, 200);
		if (strcolor.equalsIgnoreCase("brown")) return new Color(143, 83, 22);
		if (strcolor.equalsIgnoreCase("light blue")) return new Color(64, 168, 224);
		if (strcolor.equalsIgnoreCase("dark green")) return new Color(0, 128, 0);
		if (strcolor.equalsIgnoreCase("light pink")) return new Color(245, 224, 222);
		if (strcolor.equalsIgnoreCase("light green")) return new Color(128, 255, 128);
		if (strcolor.equalsIgnoreCase("very light blue")) return new Color(160, 225, 255);
		if (strcolor.equalsIgnoreCase("light yellow")) return new Color(255, 255, 128);
		return Color.white;
	}

	/**
	 * Interpret a string description of a color into a color object representing a gate
	 * color. The colors are slightly different from the literally defined colors.
	 * @param strcolor The color in words
	 * @return The Color object corresponding to the color in words. The default color
	 * is white if the color cannot be parsed
	 */
	public static Color translateGateColor(String strcolor)
	{
		if (strcolor.equalsIgnoreCase("red")) return Color.red;
		if (strcolor.equalsIgnoreCase("green")) return new Color(61, 129, 42);
		if (strcolor.equalsIgnoreCase("blue")) return new Color(30, 90, 163);
		if (strcolor.equalsIgnoreCase("yellow")) return Color.yellow;
		return Color.white;
	}

	/**
	 * Interpret a string description of a color into a color that represents the
	 * background color for a gate card of that color.
	 * @param strcolor The color in words
	 * @return The Color object corresponding to that color. The default color
     * is white if the color cannot be parsed
	 */
	public static Color translateGateBgcolor(String strcolor)
	{
	    if (strcolor.equalsIgnoreCase("red")) return new Color(255, 128, 128);
        if (strcolor.equalsIgnoreCase("green")) return new Color(181, 255, 106);
        if (strcolor.equalsIgnoreCase("blue")) return new Color(0, 224, 224);
        if (strcolor.equalsIgnoreCase("yellow")) return new Color(255, 255, 128);
        return Color.white;
	}
}
